package com.fileshare.web.service.impl;

import com.fileshare.web.dao.AuthorityRepository;
import com.fileshare.web.dao.RoleRepository;
import com.fileshare.web.dto.Bo.AuthorityBo;
import com.fileshare.web.dto.Bo.RoleBo;
import com.fileshare.web.dto.Vo.AuthorityVo;
import com.fileshare.web.dto.Vo.RoleVo;
import com.fileshare.web.entity.*;
import com.fileshare.web.service.AuthService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class AuthServiceImpl implements AuthService {
    @Resource
    private RoleRepository roleRepository;
    @Resource
    private AuthorityRepository authorityRepository;

    @Override
    public void initThirdPartyUserAuth(User thirdPartyUser) {
        //todo
    }

    @Override
    public List<String> getUserAuthStrings(User dbUser) {
        List<UserRole> userRoles = roleRepository.getUserRoles(dbUser);
        List<String> userAuthStrings = new ArrayList<>();
        for (UserRole userRole : userRoles) {
            List<RoleAuthority> roleAuthorities = authorityRepository.getRoleAuthorities(userRole);
            Role role = roleRepository.getRoleByUserRole(userRole);
            if (Objects.isNull(role)) {
                continue;
            }
            String roleName = role.getRoleName();
            if (Objects.isNull(roleAuthorities)) {
                userAuthStrings.add(roleName);
            } else {
                supplementAuthority(userAuthStrings, roleAuthorities, roleName);
            }

        }
        return userAuthStrings;
    }

    @Override
    public RoleVo addRole(RoleBo roleBo) {
        Role role = RoleBo.toEntity(roleBo);
        Date date = new Date();
        role.setCreateBy("");
        role.setUpdateAt(date);
        role.setUpdateBy("");
        role.setCreateAt(date);
        roleRepository.save(role);
        return RoleVo.fromEntity(role);
    }

    @Override
    public RoleVo removeRole(Integer id) {
        RoleBo roleBo = new RoleBo();
        Role role = RoleBo.toEntity(roleBo);
        roleRepository.deleteByRoleId(id);
        return RoleVo.fromEntity(role);
    }

    @Override
    public RoleVo updateRole(RoleBo roleBo) {
        Role role = roleRepository.findById(roleBo.getId());
        role.setRoleName(roleBo.getRoleName());
        roleRepository.update(role);
        return RoleVo.fromEntity(role);
    }

    @Override
    public List<Role> getAllRoles() {
        return roleRepository.findAllRoles();
    }

    @Override
    public List<RoleVo> getUserRoles(Integer userId) {
        List<Role> roles = roleRepository.findUserRoles(userId);
        return RoleVo.fromEntities(roles);
    }

    @Override
    public List<RoleVo> saveUserRoles(Integer userId, List<String> roles) {
        List<Role> userRoles = roleRepository.findUserRoles(userId);
        List<String> existRoles = userRoles.stream().map(Role::getRoleName).collect(Collectors.toList());
        List<String> needInsert = roles.stream().filter(role -> !existRoles.contains(role)).collect(Collectors.toList());
        List<String> needDelete = existRoles.stream().filter(role -> !roles.contains(role)).collect(Collectors.toList());
        for (String roleName : needInsert) {
            Role role = roleRepository.getRoleByUserRoleName(roleName);
            roleRepository.addUserRole(userId, role.getId());
            userRoles.add(role);
        }
        for (String roleName : needDelete) {
            Role role = roleRepository.getRoleByUserRoleName(roleName);
            roleRepository.deleteUserRole(userId, role.getId());
            userRoles.remove(role);
        }
        return RoleVo.fromEntities(userRoles);
    }

    @Override
    public List<Authority> getAllRoleAuthorities(Integer roleId) {
        return authorityRepository.findRoleAuthorities(roleId);
    }

    @Override
    public List<Authority> getAllAuthorities() {
        return authorityRepository.findAuthorities();
    }

    @Override
    public List<Authority> saveRoleAuthorities(Integer roleId, List<String> authorities) {
        List<Authority> roleAuthorities = authorityRepository.findRoleAuthorities(roleId);
        List<String> existAuthorities = roleAuthorities.stream().map(Authority::getAuthorityName).collect(Collectors.toList());
        List<String> needInsert = authorities.stream().filter(role -> !existAuthorities.contains(role)).collect(Collectors.toList());
        List<String> needDelete = existAuthorities.stream().filter(role -> !authorities.contains(role)).collect(Collectors.toList());
        for (String authorityName : needInsert) {
            Authority authority = authorityRepository.getAuthorityByAuthorityName(authorityName);
            authorityRepository.addRoleAuthority(roleId, authority.getId());
            roleAuthorities.add(authority);
        }
        for (String authorityName : needDelete) {
            Authority authority = authorityRepository.getAuthorityByAuthorityName(authorityName);
            authorityRepository.deleteUserRole(roleId, authority.getId());
            roleAuthorities.remove(authority);
        }
        return roleAuthorities;
    }

    @Override
    public AuthorityVo addAuthority(AuthorityBo authorityBo) {
        Authority authority = AuthorityBo.toEntity(authorityBo);
        Date date = new Date();
        authority.setCreateBy("");
        authority.setUpdateAt(date);
        authority.setUpdateBy("");
        authority.setCreateAt(date);
        authorityRepository.saveAuthority(authority);
        return AuthorityVo.fromEntity(authority);
    }

    @Override
    public AuthorityVo removeAuthority(Integer id) {
        AuthorityBo authorityBo = new AuthorityBo();
        Authority authority = AuthorityBo.toEntity(authorityBo);
        authorityRepository.deleteByAuthorityId(id);
        return AuthorityVo.fromEntity(authority);
    }

    @Override
    public AuthorityVo updateAuthority(AuthorityBo authorityBo) {
        Authority authority = authorityRepository.findById(authorityBo.getId());
        authority.setAuthorityName(authorityBo.getAuthorityName());
        authorityRepository.update(authority);
        return AuthorityVo.fromEntity(authority);
    }

    private void supplementAuthority(List<String> userAuthStrings, List<RoleAuthority> roleAuthorities, String roleName) {
        for (RoleAuthority roleAuthority : roleAuthorities) {
            Authority authority = authorityRepository.getAuthority(roleAuthority);
            String authString = roleName + ":" + authority.getAuthorityName();
            userAuthStrings.add(authString);
        }
    }
}
